// TechObjectDesign.cs
//
// Copyright (c) 2007 Mike Strobel
//
// This source code is subject to the terms of the Microsoft Reciprocal License (Ms-RL).
// For details, see <http://www.opensource.org/licenses/ms-rl.html>.
//
// All other rights reserved.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Windows.Markup;
using System.Xml;

using log4net;

using Supremacy.Buildings;
using Supremacy.Collections;
using Supremacy.Economy;
using Supremacy.Effects;
using Supremacy.Encyclopedia;
using Supremacy.Entities;
using Supremacy.Game;
using Supremacy.Resources;
using Supremacy.Scripting;
using Supremacy.Text;
using Supremacy.Types;
using Supremacy.Universe;
using Supremacy.Utility;

namespace Supremacy.Tech
{
    public enum BuildLimitScope : byte
    {
        None = 0,
        Civilization = 1,
        Galaxy = 2
    }

    /// <summary>
    /// Represents a group of equivalent tech object designs, any one of which
    /// fulfills a prerequisite for the construction of another tech object design.
    /// </summary>
    public interface IPrerequisiteGroup : IList<TechObjectDesign> {}

    /// <summary>
    /// Represents a group of equivalent tech object designs, any one of which
    /// fulfills a prerequisite for the construction of another tech object design.
    /// </summary>
    [Serializable]
    public class PrerequisiteGroup : CollectionBase<TechObjectDesign>, IPrerequisiteGroup {}

    /// <summary>
    /// Represents a collection of prerequisites required for the construction of a
    /// given tech object design.  Each item in the collection is actually a group of
    /// "equivalent" prerequisites (a sub-collection), and any single item in a
    /// prerequisite group satisfies the prerequisite.
    /// </summary>
    public interface IPrerequisiteCollection : IList<PrerequisiteGroup> {}

    /// <summary>
    /// Represents a collection of prerequisites required for the construction of a
    /// given tech object design.  Each item in the collection is actually a group of
    /// "equivalent" prerequisites (a sub-collection), and any single item in a
    /// prerequisite group satisfies the prerequisite.
    /// </summary>
    [Serializable]
    public class PrerequisiteCollection : CollectionBase<PrerequisiteGroup>, IPrerequisiteCollection {}

    /// <summary>
    /// The base class representing a tech object design.
    /// </summary>
    [Serializable]
    public abstract class TechObjectDesign : IBuildable, IEncyclopediaEntry, INotifyPropertyChanged
    {
        /// <summary>
        /// Represents an invalid value of the <see cref="DesignID"/> property.
        /// </summary>
        public const int InvalidDesignID = -1;

        public const string MissingImageUri = "vfs:///Resources/Images/__image_missing.png";
        
        protected const string UnknownDesignKey = "<unknown>";

        private static readonly ILog _log = GameLog.Debug.GameData;

        private ushort _designId;
        private string _key;
        //private string _name;
        //private string _description;
        private string _image;
        private int _buildCost;
        private int _maintenanceCost;
        private byte _populationHealth;
        private bool _isUniversallyAvailable;
        private readonly ScriptExpression _buildCondition;
 
        [NonSerialized]
        private ITechObjectTextDatabaseEntry _textDatabaseEntry;
        private readonly ResourceValueCollection _resourceCosts;
        private readonly TechLevelCollection _techRequirements;
        private readonly List<TechObjectDesign> _upgradableDesigns;
        private readonly List<TechObjectDesign> _obsoletedDesigns;
        private readonly PrerequisiteCollection _prerequisites;
        private readonly List<EffectGroup> _effects;

        protected static ILog Log
        {
            get { return _log; }
        }

        /// <summary>
        /// Gets or sets the design ID.
        /// </summary>
        /// <value>The design ID.</value>
        public int DesignID
        {
            get { return _designId; }
            set { _designId = (ushort)value; }
        }

        /// <summary>
        /// Gets or sets the unique key.
        /// </summary>
        /// <value>The unique key.</value>
        [ConstructorArgument("key")]
        public string Key
        {
            get { return _key; }
            set
            {
                _key = value;
                OnPropertyChanged("Key");
            }
        }

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        /// <value>The name.</value>
        public string Name
        {
            get
            {
                if (TryEnsureObjectString())
                {
                    return this.TextDatabaseEntry.Name;
                }
                return String.Empty;
            }
        }

        /// <summary>
        /// Gets the localized name.
        /// </summary>
        /// <value>The localized name.</value>
        public string LocalizedName
        {
            get { return Name; }
        }

        /// <summary>
        /// Gets or sets the description.
        /// </summary>
        /// <value>The description.</value>
        public string Description
        {
            get
            {
                if (TryEnsureObjectString())
                {
                    return this.TextDatabaseEntry.Description;
                }
                return String.Empty;
            }
        }

        protected bool TryEnsureObjectString()
        {
            //if (!_objectStringLoaded && (ObjectString == null))
            //{
            //    if (!String.IsNullOrEmpty(_key))
            //    {
            //        try { this.ObjectString = ResourceManager.Database.FindObjectString(_key); }
            //        catch {}
            //    }
            //}
            return (this.TextDatabaseEntry != null);
        }

        /// <summary>
        /// Gets the localized description.
        /// </summary>
        /// <value>The localized description.</value>
        public string LocalizedDescription
        {
            get { return Description; }
        }

        /// <summary>
        /// Gets or sets the maintenance cost of an object of this design.
        /// </summary>
        /// <value>The maintenance cost.</value>
        public int MaintenanceCost
        {
            get { return _maintenanceCost; }
            set
            {
                _maintenanceCost = Math.Max(value, 0);
                OnPropertyChanged("MaintenanceCost");
            }
        }

        /// <summary>
        /// Gets or sets the raw materials cost of an object of this design.
        /// </summary>
        /// <value>The raw materials.</value>
        public int RawMaterials
        {
            get { return BuildResourceCosts[ResourceType.RawMaterials]; }
            set
            {
                BuildResourceCosts[ResourceType.RawMaterials] = value;
                OnPropertyChanged("RawMaterials");
            }
        }

        /// <summary>
        /// Gets or sets the population health bonus of an object of this design.
        /// </summary>
        /// <value>The population health bonus.</value>
        public Percentage PopulationHealth
        {
            get { return (Percentage)Math.Round(0.01 * _populationHealth, 3); }
            set
            {
                 _populationHealth = (byte)(value * 100);
                 OnPropertyChanged("PopulationHealth");
            }
        }

        /// <summary>
        /// Gets or sets the filename of the image for this design.
        /// </summary>
        /// <value>The image filename.</value>
        public virtual string Image
        {
            get
            {
                if (IsImageDefined)
                {
                    if (IsImageLinked)
                    {
                        TechObjectDesign linkSource = this.ImageLinkSource;
                        if (linkSource != null)
                            return linkSource.Image;
                    }
                    else
                    {
                        return ResourceManager.GetResourceUri(
                            String.Format("Resources/Images/TechObjects/{0}",
                            Path.GetFileName(_image))).ToString();
                    }
                }

                var localPath = ResourceManager.GetResourcePath(
                    String.Format(
                        "vfs:///Resources/Images/TechObjects/{0}",
                        _key.ToLowerInvariant()));

                if (File.Exists(localPath + ".png"))
                    return ResourceManager.GetResourceUri(localPath + ".png").ToString();

                if (File.Exists(localPath + ".jpg"))
                    return ResourceManager.GetResourceUri(localPath + ".jpg").ToString();

                return MissingImageUri;
            }
            set
            {
                _image = value;
                OnPropertyChanged("Image");
                OnPropertyChanged("ImageLinkSource");
                OnPropertyChanged("IsImageDefined");
                OnPropertyChanged("IsImageAutomatic");
                OnPropertyChanged("IsImageLinked");
            }
        }

        public ScriptExpression BuildCondition
        {
            get { return _buildCondition; }
        }

        /// <summary>
        /// Gets a value indicating whether an image is explicitly defined.
        /// </summary>
        /// <value>
        /// <c>true</c> if an image is explicitly defined; otherwise, <c>false</c>.
        /// </value>
        public bool IsImageDefined
        {
            get { return (_image != null); }
        }

        /// <summary>
        /// Gets a value indicating whether the image is the default (automatic).
        /// </summary>
        /// <value>
        /// <c>true</c> if the image is the default (automatic); otherwise, <c>false</c>.
        /// </value>
        public bool IsImageAutomatic
        {
            get { return (_image == null); }
        }

        /// <summary>
        /// Gets a value indicating whether the image is linked to another object.
        /// </summary>
        /// <value>
        /// <c>true</c> if the image is linked to another object; otherwise, <c>false</c>.
        /// </value>
        public bool IsImageLinked
        {
            get { return ((_image != null) && _image.StartsWith("@")); }
        }

        public TechObjectDesign ImageLinkSource
        {
            get
            {
                if (!String.IsNullOrEmpty(_image) && _image.StartsWith("@") && (_image.Length > 1))
                {
                    string linkKey = _image.Substring(1);
                    TechObjectDesign linkDesign = GameContext.Current.TechDatabase[linkKey];
                    if (linkDesign != null)
                        return linkDesign;
                }
                return null;
            }
        }

        /// <summary>
        /// Gets the list of prerequisite groups for this design.
        /// </summary>
        /// <value>The prerequisite groups.</value>
        /// <remarks>
        /// The return value is a list containing lists of equivalent prerequisites.
        /// That is, for each list in the return value, at least one of the items
        /// in the list must be present in order to construct an object of this design.
        /// </remarks>
        public IPrerequisiteCollection Prerequisites
        {
            get { return _prerequisites; }
        }

        public IList<EffectGroup> Effects
        {
            get { return _effects; }
        }

        /// <summary>
        /// Gets the list of designs to which an object of this design is upgradable.
        /// </summary>
        /// <value>The list of designs.</value>
        public IList<TechObjectDesign> UpgradableDesigns
        {
            get { return _upgradableDesigns; }
        }

        /// <summary>
        /// Gets the list of designs rendered obsolete by this design.
        /// </summary>
        /// <value>The list of designs.</value>
        public IList<TechObjectDesign> ObsoletedDesigns
        {
            get { return _obsoletedDesigns; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="TechObjectDesign"/> is universally available.
        /// </summary>
        /// <value>
        /// <c>true</c> if universally available; otherwise, <c>false</c>.
        /// </value>
        public bool IsUniversallyAvailable
        {
            get { return _isUniversallyAvailable; }
            set
            {
                _isUniversallyAvailable = value;
                OnPropertyChanged("IsUniversallyAvailable");
            }
        }

        protected TechObjectDesign(string key)
        {
            if (string.IsNullOrWhiteSpace(key))
                throw new ArgumentException(SR.ArgumentException_ValueMustBeNonEmptyString);
            _key = key;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TechObjectDesign"/> class.
        /// </summary>
        internal TechObjectDesign()
        {
            _buildCost = 0;
            _resourceCosts = new ResourceValueCollection();
            _techRequirements = new TechLevelCollection();
            _upgradableDesigns = new List<TechObjectDesign>();
            _obsoletedDesigns = new List<TechObjectDesign>();
            _prerequisites = new PrerequisiteCollection();
            _effects = new List<EffectGroup>();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TechObjectDesign"/> class from XML data.
        /// </summary>
        /// <param name="element">The XML element.</param>
        protected internal TechObjectDesign(XmlElement element)
            : this()
        {
            // ReSharper disable PossibleNullReferenceException

            _key = element.GetAttribute("Key");
            //_name = element["Name"].InnerText.Trim();
            //_description = element["Description"].InnerText.Trim();
            if (element["Image"] != null)
            {
                _image = element["Image"].InnerText.Trim();
            }
            if (element["TechRequirements"] != null)
            {
                foreach (XmlNode xmlTechReqNode in element["TechRequirements"].ChildNodes)
                {
                    XmlElement xmlTechReq = xmlTechReqNode as XmlElement;
                    if (xmlTechReq != null)
                    {
                        TechCategory category = (TechCategory)Enum.Parse(
                            typeof(TechCategory),
                            xmlTechReq.Name);
                        _techRequirements[category] = Number.ParseInt32(xmlTechReq.InnerText.Trim());
                    }
                }
                //_description = element["Description"].InnerText.Trim();
            }
            if (element["BuildCost"] != null)
            {
                _buildCost = Number.ParseInt32(element["BuildCost"].InnerText.Trim());
            }
            if (element["RawMaterials"] != null)
            {
                _resourceCosts[ResourceType.RawMaterials] = Number.ParseInt32(
                    element["RawMaterials"].InnerText.Trim());
            }
            if (element["MaintenanceCost"] != null)
            {
                _maintenanceCost = Number.ParseInt32(element["MaintenanceCost"].InnerText.Trim());
            }
            if (element["PopulationHealth"] != null)
            {
                _populationHealth = Number.ParseByte(element["PopulationHealth"].InnerText.Trim());
            }
            if (element["IsUniversallyAvailable"] != null)
            {
                _isUniversallyAvailable = Number.ParseBoolean(element["IsUniversallyAvailable"].InnerText.Trim().ToLowerInvariant());
            }
            if (element["BuildCondition"] != null)
            {
                _buildCondition = new ScriptExpression
                                  {
                                      Parameters = new ScriptParameters(
                                          new ScriptParameter("$source", typeof(Colony)),
                                          new ScriptParameter("$design", GetType())),
                                      ScriptCode = element["BuildCondition"].InnerText.Trim()
                                  };
            }
            // ReSharper restore PossibleNullReferenceException
        }

        public void LinkImageToDesign(TechObjectDesign design)
        {
            if (design == null)
                throw new ArgumentNullException("design");
            if (design == this)
                throw new ArgumentException("Cannot link to self.");
            this.Image = "@" + design.Key;
        }

        /// <summary>
        /// Compacts this instance to reduce serialization footprint.
        /// </summary>
        public virtual void Compact()
        {
            _obsoletedDesigns.TrimExcess();
            _upgradableDesigns.TrimExcess();
            foreach (var prerequisite in _prerequisites)
                prerequisite.TrimExcess();
            _prerequisites.TrimExcess();
            _effects.TrimExcess();
        }

        /// <summary>
        /// Appends the XML data for this instance.
        /// </summary>
        /// <param name="baseElement">The base XML element.</param>
        protected internal virtual void AppendXml(XmlElement baseElement)
        {
            XmlDocument doc = baseElement.OwnerDocument;
            XmlElement newElement;
            bool hasTechRequirements = false;

            baseElement.SetAttribute("Key", _key);

            //newElement = doc.CreateElement("Name");
            //newElement.InnerText = _name;
            //baseElement.AppendChild(newElement);

            //newElement = doc.CreateElement("Description");
            //newElement.InnerText = _description;
            //baseElement.AppendChild(newElement);

            if (_image != null)
            {
                newElement = doc.CreateElement("Image");
                newElement.InnerText = _image;
                baseElement.AppendChild(newElement);
            }

            newElement = doc.CreateElement("TechRequirements");
            foreach (TechCategory category in Enum.GetValues(typeof(TechCategory)))
            {
                if (_techRequirements[category] > 0)
                {
                    XmlElement subElement = doc.CreateElement(category.ToString());
                    subElement.InnerText = _techRequirements[category].ToString();
                    newElement.AppendChild(subElement);
                    hasTechRequirements = true;
                }
            }
            if (hasTechRequirements)
            {
                baseElement.AppendChild(newElement);
            }

            newElement = doc.CreateElement("BuildCost");
            newElement.InnerText = _buildCost.ToString();
            baseElement.AppendChild(newElement);

            if (RawMaterials > 0)
            {
                newElement = doc.CreateElement("RawMaterials");
                newElement.InnerText = _resourceCosts[ResourceType.RawMaterials].ToString();
                baseElement.AppendChild(newElement);
            }

            if (MaintenanceCost > 0)
            {
                newElement = doc.CreateElement("MaintenanceCost");
                newElement.InnerText = _maintenanceCost.ToString();
                baseElement.AppendChild(newElement);
            }

            if (PopulationHealth > 0)
            {
                newElement = doc.CreateElement("PopulationHealth");
                newElement.InnerText = _populationHealth.ToString();
                baseElement.AppendChild(newElement);
            }

            newElement = doc.CreateElement("IsUniversallyAvailable");
            newElement.InnerText = _isUniversallyAvailable.ToString().ToLowerInvariant();
            baseElement.AppendChild(newElement);

            if (_prerequisites.Count > 0)
            {
                newElement = doc.CreateElement("Prerequisites");
                foreach (var prereqList in _prerequisites)
                {
                    XmlElement subElement = doc.CreateElement("EquivalentPrerequisites");
                    foreach (TechObjectDesign prereq in prereqList)
                    {
                        XmlElement subSubElement = doc.CreateElement("Prerequisite");
                        subSubElement.InnerText = prereq.Key;
                        subElement.AppendChild(subSubElement);
                    }
                    if (subElement.ChildNodes.Count > 0)
                    {
                        newElement.AppendChild(subElement);
                    }
                }
                if (newElement.ChildNodes.Count > 0)
                    baseElement.AppendChild(newElement);
            }

            if (_obsoletedDesigns.Count > 0)
            {
                newElement = doc.CreateElement("ObsoletedItems");
                foreach (TechObjectDesign design in _obsoletedDesigns)
                {
                    XmlElement subElement = doc.CreateElement("ObsoletedItem");
                    subElement.InnerText = design.Key;
                    newElement.AppendChild(subElement);
                }
                if (newElement.ChildNodes.Count > 0)
                    baseElement.AppendChild(newElement);
            }

            if (_upgradableDesigns.Count > 0)
            {
                newElement = doc.CreateElement("UpgradeOptions");
                foreach (TechObjectDesign design in _upgradableDesigns)
                {
                    XmlElement subElement = doc.CreateElement("UpgradeOption");
                    subElement.InnerText = design.Key;
                    newElement.AppendChild(subElement);
                }
                if (newElement.ChildNodes.Count > 0)
                    baseElement.AppendChild(newElement);
            }
        }

        /// <summary>
        /// Gets the credits and resources that should be returned to a civilization when an object
        /// of this design is scrapped.
        /// </summary>
        /// <param name="credits">The credits.</param>
        /// <param name="resources">The resources.</param>
        protected internal virtual void GetScrapReturn(out int credits, out ResourceValueCollection resources)
        {
            var returnsTable = GameContext.Current.Tables.ResourceTables["ScrapReturns"];
            double multiplier = Number.ParseDouble(returnsTable[0][0]);
            credits = (int)Math.Floor(multiplier * BuildCost);
            resources = new ResourceValueCollection();
            foreach (ResourceType resource in Enum.GetValues(typeof(ResourceType)))
            {
                multiplier = Number.ParseDouble(returnsTable[resource.ToString()][0]);
                resources[resource] += (int)Math.Floor(
                    multiplier * BuildResourceCosts[resource]);
            }
        }

        /// <summary>
        /// Spawns an instance of an object of this design at the specified location.
        /// </summary>
        /// <param name="location">The location.</param>
        /// <param name="owner">The owner.</param>
        public abstract bool TrySpawn(MapLocation location, Civilization owner);

        protected virtual bool CanSpawn(
            MapLocation location,
            Civilization owner,
            bool requireSectorOwned = false,
            bool requireStarSystem = false,
            bool requireColony = false)
        {
            var civManager = GameContext.Current.CivilizationManagers[owner];
            if (civManager == null)
            {
                Log.ErrorFormat(
                    "Cannot spawn {0} at location {1} because " +
                    "owner {2} is not active in this game.",
                    this.Key,
                    location,
                    owner.Key);

                return false;
            }

            var sector = GameContext.Current.Universe.Map[location];

            if (requireSectorOwned &&
                !Equals(sector.Owner, owner))
            {
                Log.ErrorFormat(
                    "Cannot spawn {0} at location {1} because " +
                    "the sector is not owned by {2}.",
                    this.Key,
                    location,
                    owner.Key);

                return false;
            }

            if (requireStarSystem || requireColony)
            {
                var system = sector.System;
                if (system == null)
                {
                    Log.ErrorFormat(
                        "Cannot spawn {0} at location {1} because " +
                        "there is no star system at that location.",
                        this.Key,
                        location);

                    return false;
                }

                if (requireColony && !system.HasColony)
                {
                    Log.ErrorFormat(
                        "Cannot spawn {0} at location {1} because " +
                        "there is no colony at that location.",
                        this.Key,
                        location);

                    return false;
                }
            }

            return true;
        }

        #region IBuildable Members
        /// <summary>
        /// Gets the resource costs for building an object of this design..
        /// </summary>
        /// <value>The resource costs.</value>
        public ResourceValueCollection BuildResourceCosts
        {
            get { return _resourceCosts; }
        }

        /// <summary>
        /// Gets or sets the industry cost for building an object of this design.
        /// </summary>
        /// <value>The build cost.</value>
        public int BuildCost
        {
            get { return _buildCost; }
            set { _buildCost = Math.Max(value, 0); }
        }

        /// <summary>
        /// Gets the tech level requirements for building an object of this design.
        /// </summary>
        /// <value>The tech level requirements.</value>
        public TechLevelCollection TechRequirements
        {
            get { return _techRequirements; }
        }
        #endregion

        /// <summary>
        /// Implements the operator ==.
        /// </summary>
        /// <param name="a">The first operand.</param>
        /// <param name="b">The second operand.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator ==(TechObjectDesign a, TechObjectDesign b)
        {
            if (ReferenceEquals(a, b))
                return true;
            if (((object)a == null) || ((object)b == null))
                return false;
            return (a._key == b._key);
        }

        /// <summary>
        /// Implements the operator !=.
        /// </summary>
        /// <param name="a">The first operand.</param>
        /// <param name="b">The second operand.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator !=(TechObjectDesign a, TechObjectDesign b)
        {
            if (ReferenceEquals(a, b))
                return false;
            if (((object)a == null) || ((object)b == null))
                return true;
            return (a._key != b._key);
        }

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="TechObjectDesign"/>.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="TechObjectDesign"/>.</param>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="TechObjectDesign"/>; otherwise, false.
        /// </returns>
        /// <filterPriority>2</filterPriority>
        public override bool Equals(object obj)
        {
            return Equals(obj as TechObjectDesign);
        }

        /// <summary>
        /// Determines whether the specified <see cref="TechObjectDesign"/> is equal to the current <see cref="TechObjectDesign"/>.
        /// </summary>
        /// <param name="design">The <see cref="TechObjectDesign"/> to compare with the current <see cref="TechObjectDesign"/>.</param>
        /// <returns>
        /// true if the specified <see cref="TechObjectDesign"/> is equal to the current <see cref="TechObjectDesign"/>; otherwise, false.
        /// </returns>
        /// <filterPriority>2</filterPriority>
        public virtual bool Equals(TechObjectDesign design)
        {
            if (design == null)
                return false;
            return Equals(_key, design._key);
        }

        /// <summary>
        /// Serves as a hash function for a particular design.
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"/>.
        /// </returns>
        /// <filterPriority>2</filterPriority>
        public override int GetHashCode()
        {
            return _designId;
        }

        /// <summary>
        /// Returns a <see cref="T:System.String"/> that represents the current <see cref="TechObjectDesign"/>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"/> that represents the current <see cref="TechObjectDesign"/>.
        /// </returns>
        /// <filterPriority>2</filterPriority>
        public override string ToString()
        {
            if (!String.IsNullOrEmpty(this.Name))
                return this.Name;
            return _key;
        }

        #region IEncyclopediaEntry Members
        /// <summary>
        /// Gets the heading displayed in the Encyclopedia index.
        /// </summary>
        /// <value>The heading.</value>
        public string EncyclopediaHeading
        {
            get { return Name; }
        }

        /// <summary>
        /// Gets the text displayed in the Encyclopedia entry.
        /// </summary>
        /// <value>The text.</value>
        public string EncyclopediaText
        {
            get { return ResourceManager.GetString(Description); }
        }

        /// <summary>
        /// Gets the image displayed in the Encyclopedia entry.
        /// </summary>
        /// <value>The image.</value>
        public string EncyclopediaImage
        {
            get { return Image; }
        }

        /// <summary>
        /// Gets the encyclopedia category under which the entry appears.
        /// </summary>
        /// <value>The encyclopedia category.</value>
        public virtual EncyclopediaCategory EncyclopediaCategory
        {
            get { return EncyclopediaCategory.None; }
        }

        protected internal ITechObjectTextDatabaseEntry TextDatabaseEntry
        {
            get { return this._textDatabaseEntry; }
            set
            {
                //_objectStringLoaded = true;
                this._textDatabaseEntry = value;
                OnPropertyChanged("TextDatabaseEntry");
                OnPropertyChanged("Name");
                OnPropertyChanged("Description");
                OnPropertyChanged("EncyclopediaHeading");
                OnPropertyChanged("EncyclopediaText");
            }
        }
        #endregion

        #region INotifyPropertyChanged Members
        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        [field:NonSerialized]
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Raises the <see cref="PropertyChanged"/> event.
        /// </summary>
        /// <param name="propertyName">Name of the property that changed.</param>
        protected internal void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
        #endregion
    }
}